package com.wd.live.tool.socket.wsmanager

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.os.Handler
import android.os.Looper
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okio.ByteString
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock

/**
 * @Title:
 * @ClassName: com.wd.base.utils.socket.wsmanager.WsManager.java
 * @author: lowrance
 * @date:  2022/9/21 18:02
 * @Description:    WsManager的管理实现类
 */
class WsManager(builder: Builder) : IWsManager {
    //重连自增步长
    private val reConnectInterval: Int

    //最大重连间隔
    private val reConnectMaxTime: Long
    private val mContext: Context
    private val wsUrl: String?
    override var webSocket: WebSocket? = null
    private var mOkHttpClient: OkHttpClient?
    private var mRequest: Request? = null

    @get:Synchronized
    @set:Synchronized
    override var currentStatus = WsStatus.DISCONNECTED //websocket连接状态

    //是否需要断线自动重连
    private val isNeedReconnect: Boolean

    //是否为手动关闭websocket连接
    private var isManualClose = false
    private var wsStatusListener: WsStatusListener? = null
    private val mLock: Lock
    private val wsMainHandler = Handler(Looper.getMainLooper())

    //重连次数
    private var reconnectCount = 0
    private val headers: Headers?
    private val netStatusReceiver: NetStatusReceiver
    private val reconnectRunnable = Runnable {
        wsStatusListener?.onReconnect()
        buildConnect()
    }
    private val mWebSocketListener: WebSocketListener = object : WebSocketListener() {
        override fun onOpen(webSocket: WebSocket, response: Response) {
            this@WsManager.webSocket = webSocket
            currentStatus = WsStatus.CONNECTED
            connected()
            wsStatusListener?.let {
                if (Looper.myLooper() != Looper.getMainLooper()) {
                    wsMainHandler.post { it.onOpen(response) }
                } else {
                    it.onOpen(response)
                }
            }
        }

        override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
            wsStatusListener?.let {
                if (Looper.myLooper() != Looper.getMainLooper()) {
                    wsMainHandler.post { it.onMessage(bytes) }
                } else {
                    it.onMessage(bytes)
                }
            }
        }

        override fun onMessage(webSocket: WebSocket, text: String) {
            wsStatusListener?.let {
                if (Looper.myLooper() != Looper.getMainLooper()) {
                    wsMainHandler.post { it.onMessage(text) }
                } else {
                    it.onMessage(text)
                }
            }
        }

        override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
            wsStatusListener?.let {
                if (Looper.myLooper() != Looper.getMainLooper()) {
                    wsMainHandler.post { it.onClosing(code, reason) }
                } else {
                    it.onClosing(code, reason)
                }
            }
        }

        override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
            wsStatusListener?.let {
                if (Looper.myLooper() != Looper.getMainLooper()) {
                    wsMainHandler.post { it.onClosed(code, reason) }
                } else {
                    it.onClosed(code, reason)
                }
            }
        }

        override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
            wsStatusListener?.let {
                if (Looper.myLooper() != Looper.getMainLooper()) {
                    wsMainHandler.post { it.onFailure(t, response) }
                } else {
                    it.onFailure(t, response)
                }
            }
            tryReconnect()
        }
    }

    init {
        mContext = builder.mContext
        wsUrl = builder.wsUrl
        isNeedReconnect = builder.needReconnect
        mOkHttpClient = builder.mOkHttpClient
        reConnectInterval = builder.reConnectInterval
        reConnectMaxTime = builder.reConnectMaxTime
        headers = builder.headers
        mLock = ReentrantLock()
        netStatusReceiver = NetStatusReceiver()
    }

    private fun initWebSocket() {
        if (mOkHttpClient == null) {
            mOkHttpClient = OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .build()
        }
        if (mRequest == null) {
            mRequest = if (null == headers) {
                wsUrl?.let {
                    Request.Builder()
                        .url(it)
                        .build()
                }
            } else {
                wsUrl?.let {
                    Request.Builder()
                        .url(it)
                        .headers(headers)
                        .build()
                }
            }
        }
        try {
            mLock.lockInterruptibly()
            try {
                mOkHttpClient!!.newWebSocket(mRequest!!, mWebSocketListener)
            } finally {
                mLock.unlock()
            }
        } catch (e: InterruptedException) {
        }
    }

    fun setWsStatusListener(wsStatusListener: WsStatusListener?) {
        this.wsStatusListener = wsStatusListener
    }

    @get:Synchronized
    override val isWsConnected: Boolean
        get() = currentStatus == WsStatus.CONNECTED

    override fun startConnect() {
        isManualClose = false
        buildConnect()
        val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        mContext.registerReceiver(netStatusReceiver, filter)
    }

    override fun stopConnect() {
        isManualClose = true
        disconnect()
        mContext.unregisterReceiver(netStatusReceiver)
    }

    private fun tryReconnect() {
        if (!isNeedReconnect or isManualClose) {
            return
        }
        if (!isNetworkConnected(mContext)) {
            currentStatus = WsStatus.DISCONNECTED
            return
        }
        currentStatus = WsStatus.RECONNECT
        val delay = (reconnectCount * reConnectInterval).toLong()
        wsMainHandler
            .postDelayed(
                reconnectRunnable,
                if (delay > reConnectMaxTime) reConnectMaxTime else delay
            )
        reconnectCount++
    }

    private fun cancelReconnect() {
        wsMainHandler.removeCallbacks(reconnectRunnable)
        reconnectCount = 0
    }

    private fun connected() {
        cancelReconnect()
    }

    private fun disconnect() {
        if (currentStatus == WsStatus.DISCONNECTED) {
            return
        }
        cancelReconnect()
        webSocket?.let {
            val isClosed = it.close(WsStatus.CODE.NORMAL_CLOSE, WsStatus.TIP.NORMAL_CLOSE)
            //非正常关闭连接
            if (!isClosed) {
                wsStatusListener?.onClosed(
                    WsStatus.CODE.ABNORMAL_CLOSE,
                    WsStatus.TIP.ABNORMAL_CLOSE
                )
            }
        }
        currentStatus = WsStatus.DISCONNECTED
    }

    @Synchronized
    private fun buildConnect() {
        if (!isNetworkConnected(mContext)) {
            currentStatus = WsStatus.DISCONNECTED
            return
        }
        when (currentStatus) {
            WsStatus.CONNECTED, WsStatus.CONNECTING -> {}
            else -> {
                currentStatus = WsStatus.CONNECTING
                initWebSocket()
            }
        }
    }

    //发送消息
    override fun sendMessage(msg: String?): Boolean {
        return send(msg)
    }

    override fun sendMessage(byteString: ByteString?): Boolean {
        return send(byteString)
    }

    private fun send(msg: Any?): Boolean {
        var isSend = false
        if (webSocket != null && currentStatus == WsStatus.CONNECTED) {
            if (msg is String) {
                isSend = webSocket!!.send((msg as String?)!!)
            } else if (msg is ByteString) {
                isSend = webSocket!!.send((msg as ByteString?)!!)
            }
            //发送消息失败，尝试重连
            if (!isSend) {
                tryReconnect()
            }
        }
        return isSend
    }

    //检查网络是否连接
    private fun isNetworkConnected(context: Context?): Boolean {
        context?.let {
            val mConnectivityManager = it
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val mNetworkInfo = mConnectivityManager
                .activeNetworkInfo
            if (mNetworkInfo != null) {
                return mNetworkInfo.isAvailable
            }
        }
        return false
    }

    class Builder(val mContext: Context) {
        var wsUrl: String? = null
        var needReconnect = true
        var reConnectInterval = 10 * 1000
        var reConnectMaxTime = (120 * 1000).toLong()
        var mOkHttpClient: OkHttpClient? = null
        var headers: Headers? = null
        fun wsUrl(`val`: String?): Builder {
            wsUrl = `val`
            return this
        }

        fun client(`val`: OkHttpClient?): Builder {
            mOkHttpClient = `val`
            return this
        }

        fun needReconnect(`val`: Boolean): Builder {
            needReconnect = `val`
            return this
        }

        fun setReconnectInterval(`val`: Int): Builder {
            reConnectInterval = `val`
            return this
        }

        fun setReconnectIMaxTime(`val`: Long): Builder {
            reConnectMaxTime = `val`
            return this
        }

        fun setHeaders(headers: Headers?): Builder {
            this.headers = headers
            return this
        }

        fun build(): WsManager {
            return WsManager(this)
        }
    }

    inner class NetStatusReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val action = intent.action
            if (ConnectivityManager.CONNECTIVITY_ACTION == action) {

                // 获取网络连接管理器
                val connectivityManager = mContext
                    .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
                // 获取当前网络状态信息
                val info = connectivityManager.activeNetworkInfo
                if (info != null && info.isAvailable) {
                    cancelReconnect()
                    tryReconnect()
                }
            }
        }
    }
}